ποΈGitΠ―ΡΠ°ποΈ
Node / meshtastic / Meshtastic-Android / files / core / takserver / src / commonTest / kotlin / org / meshtastic / core / takserver / TakMeshTestRunnerTest.kt
Displaying Raw β’ Download
core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TakMeshTestRunnerTest.kt 65a1f5ce4d0e4b8eafcd0bbfccafd41dc6ee888a (65a1f5ce) Text, 8.79 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.takserver
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableStateFlow
Tff7b72import T7ee787kotlinx.coroutines.test.UnconfinedTestDispatcher
Tff7b72import T7ee787kotlinx.coroutines.test.runTest
Tff7b72import T7ee787org.meshtastic.core.di.CoroutineDispatchers
Tff7b72import T7ee787org.meshtastic.core.repository.MeshConfigHandler
Tff7b72import T7ee787org.meshtastic.core.repository.RadioSessionContext
Tff7b72import T7ee787org.meshtastic.core.testing.FakeCommandSender
Tff7b72import T7ee787org.meshtastic.core.testing.FakeNodeRepository
Tff7b72import T7ee787org.meshtastic.core.testing.FakeServiceRepository
Tff7b72import T7ee787org.meshtastic.core.testing.FakeTakPrefs
Tff7b72import T7ee787org.meshtastic.core.testing.TestDataFactory
Tff7b72import T7ee787org.meshtastic.proto.Channel
Tff7b72import T7ee787org.meshtastic.proto.Config
Tff7b72import T7ee787org.meshtastic.proto.DeviceUIConfig
Tff7b72import T7ee787org.meshtastic.proto.LoRaRegionPresetMap
Tff7b72import T7ee787org.meshtastic.proto.LocalConfig
Tff7b72import T7ee787org.meshtastic.proto.LocalModuleConfig
Tff7b72import T7ee787org.meshtastic.proto.ModuleConfig
Tff7b72import T7ee787org.meshtastic.proto.PortNum
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertTrue
T8b949e/**
* Regression coverage for the self-test blind spot found while investigating #6583: [TakMeshTestRunner] must exercise
* the SAME real dispatch pipeline ([TAKMeshIntegration.sendCoTToMeshV1] / `sendCoTToMeshV2`) that live TAK traffic
* uses, not a hardcoded v2-only shortcut β and it must do so for BOTH protocol versions regardless of whichever
* firmware a test radio happens to report, since [TAKMeshIntegration.useTakV2] gates real traffic on the connected
* radio's firmware and older firmware silently falls back to the much more limited v1 schema.
*/
Tff7b72class T56d364TakMeshTestRunnerTest Tb4b4b4{
Tff7b72private Tff7b72class T56d364FakeMeshConfigHandler Tb4b4b4: Te6edf3MeshConfigHandler Tb4b4b4{
Tff7b72override Tff7b72val Te6edf3localConfig Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3LocalConfigTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3moduleConfig Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3LocalModuleConfigTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffhandleDeviceConfigTb4b4b4(Te6edf3configTb4b4b4: Te6edf3ConfigTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tff7b72= Tff7b72true
Tff7b72override Tff7b72fun Td2a8ffhandleModuleConfigTb4b4b4(Te6edf3configTb4b4b4: Te6edf3ModuleConfigTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tff7b72= Tff7b72true
Tff7b72override Tff7b72fun Td2a8ffhandleChannelTb4b4b4(Te6edf3channelTb4b4b4: Te6edf3ChannelTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tff7b72= Tff7b72true
Tff7b72override Tff7b72fun Td2a8ffhandleDeviceUIConfigTb4b4b4(Te6edf3configTb4b4b4: Te6edf3DeviceUIConfigTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tff7b72= Tff7b72true
Tff7b72override Tff7b72fun Td2a8ffhandleRegionPresetsTb4b4b4(Te6edf3mapTb4b4b4: Te6edf3LoRaRegionPresetMapTb4b4b4, Te6edf3sessionTb4b4b4: Te6edf3RadioSessionContextTb4b4b4) Tff7b72= Tff7b72true
Tb4b4b4}
Tff7b72private Tff7b72class T56d364HarnessTb4b4b4(Te6edf3firmwareVersionTb4b4b4: Tffa657String?Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3serverManager Tff7b72= Te6edf3FakeTAKServerManagerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3commandSender Tff7b72= Te6edf3FakeCommandSenderTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3serviceRepository Tff7b72= Te6edf3FakeServiceRepositoryTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3meshConfigHandler Tff7b72= Te6edf3FakeMeshConfigHandlerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3nodeRepository Tff7b72=
Te6edf3FakeNodeRepositoryTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3apply Tb4b4b4{
Te6edf3setMyNodeInfoTb4b4b4(Te6edf3TestDataFactoryTb4b4b4.Te6edf3createMyNodeInfoTb4b4b4(Te6edf3firmwareVersion Tff7b72= Te6edf3firmwareVersionTb4b4b4)Tb4b4b4)
Tb4b4b4}
Tff7b72val Te6edf3takPrefs Tff7b72= Te6edf3FakeTakPrefsTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3dispatcher Tff7b72= Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3broadcaster Tff7b72=
Te6edf3MeshToCotBroadcasterTb4b4b4(
Te6edf3serverManagerTb4b4b4,
Te6edf3nodeRepositoryTb4b4b4,
Te6edf3takPrefsTb4b4b4,
Te6edf3CoroutineDispatchersTb4b4b4(Te6edf3io Tff7b72= Te6edf3dispatcherTb4b4b4, Te6edf3main Tff7b72= Te6edf3dispatcherTb4b4b4, Te6edf3default Tff7b72= Te6edf3dispatcherTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3integration Tff7b72=
Te6edf3TAKMeshIntegrationTb4b4b4(
Te6edf3takServerManager Tff7b72= Te6edf3serverManagerTb4b4b4,
Te6edf3commandSender Tff7b72= Te6edf3commandSenderTb4b4b4,
Te6edf3serviceRepository Tff7b72= Te6edf3serviceRepositoryTb4b4b4,
Te6edf3meshConfigHandler Tff7b72= Te6edf3meshConfigHandlerTb4b4b4,
Te6edf3nodeRepository Tff7b72= Te6edf3nodeRepositoryTb4b4b4,
Te6edf3meshToCotBroadcaster Tff7b72= Te6edf3broadcasterTb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3runner Tff7b72= Te6edf3TakMeshTestRunnerTb4b4b4(Te6edf3integrationTb4b4b4)
Tb4b4b4}
T8b949e// ββ runAll() covers both protocols ββββββββββββββββββββββββββββββββββββββ
Tf0883e@Test
Tff7b72fun Td2a8ff`runAll exercises every fixture on both v1 and v2`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// Firmware is irrelevant here β the runner forces the protocol per pass rather than
T8b949e// reading Capabilities.supportsTakV2, which is exactly the bug this test guards against.
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Te6edf3firmwareVersion Tff7b72= Tff7b72nullTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3runnerTb4b4b4.Te6edf3runAllTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3results Tff7b72= Te6edf3hTb4b4b4.Te6edf3runnerTb4b4b4.Te6edf3resultsTb4b4b4.Te6edf3value
Te6edf3assertEqualsTb4b4b4(Te6edf3TakMeshTestRunnerTb4b4b4.Te6edf3FIXTURE_NAMESTb4b4b4.Te6edf3size Tff7b72* T79c0ff2Tb4b4b4, Te6edf3resultsTb4b4b4.Te6edf3sizeTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3TakMeshTestRunnerTb4b4b4.Te6edf3FIXTURE_NAMESTb4b4b4.Te6edf3sizeTb4b4b4, Te6edf3resultsTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3protocol Tff7b72=Tff7b72= Te6edf3TakProtocolTb4b4b4.Te6edf3V2 Tb4b4b4}Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3TakMeshTestRunnerTb4b4b4.Te6edf3FIXTURE_NAMESTb4b4b4.Te6edf3sizeTb4b4b4, Te6edf3resultsTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3protocol Tff7b72=Tff7b72= Te6edf3TakProtocolTb4b4b4.Te6edf3V1 Tb4b4b4}Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`v2 pass sends on ATAK_PLUGIN_V2 regardless of connected firmware`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// Connected "radio" reports legacy firmware β if the runner still read useTakV2() instead of
T8b949e// forcing the protocol, this would incorrectly downgrade the v2 pass to v1.
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Te6edf3firmwareVersion Tff7b72= Ta5d6ff"Ta5d6ff2.7.0.0Ta5d6ff"Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3runnerTb4b4b4.Te6edf3runAllTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3v2Results Tff7b72= Te6edf3hTb4b4b4.Te6edf3runnerTb4b4b4.Te6edf3resultsTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3protocol Tff7b72=Tff7b72= Te6edf3TakProtocolTb4b4b4.Te6edf3V2 Tb4b4b4}
Tff7b72val Te6edf3v2Sent Tff7b72= Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3dataType Tff7b72=Tff7b72= Te6edf3PortNumTb4b4b4.Te6edf3ATAK_PLUGIN_V2Tb4b4b4.Te6edf3value Tb4b4b4}
Te6edf3assertEqualsTb4b4b4(Te6edf3v2ResultsTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3passed Tb4b4b4}Tb4b4b4, Te6edf3v2SentTb4b4b4, Ta5d6ff"Ta5d6ffevery passing v2 result must correspond to a v2 sendTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3v2Sent Tff7b72> T79c0ff0Tb4b4b4, Ta5d6ff"Ta5d6ffat least one fixture must round-trip through the real v2 pipelineTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`v1 pass sends on legacy ATAK_PLUGIN regardless of connected firmware`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
T8b949e// Connected "radio" reports v2-capable firmware β if the runner still read useTakV2() instead
T8b949e// of forcing the protocol, this would incorrectly upgrade the v1 pass to v2, hiding the exact
T8b949e// blind spot reported in #6583.
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Te6edf3firmwareVersion Tff7b72= Ta5d6ff"Ta5d6ff2.8.0.0Ta5d6ff"Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3runnerTb4b4b4.Te6edf3runAllTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3v1Results Tff7b72= Te6edf3hTb4b4b4.Te6edf3runnerTb4b4b4.Te6edf3resultsTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3protocol Tff7b72=Tff7b72= Te6edf3TakProtocolTb4b4b4.Te6edf3V1 Tb4b4b4}
Tff7b72val Te6edf3v1Sent Tff7b72= Te6edf3hTb4b4b4.Te6edf3commandSenderTb4b4b4.Te6edf3sentPacketsTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3dataType Tff7b72=Tff7b72= Te6edf3PortNumTb4b4b4.Te6edf3ATAK_PLUGINTb4b4b4.Te6edf3value Tb4b4b4}
Te6edf3assertEqualsTb4b4b4(Te6edf3v1ResultsTb4b4b4.Te6edf3count Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3passed Tb4b4b4}Tb4b4b4, Te6edf3v1SentTb4b4b4, Ta5d6ff"Ta5d6ffevery passing v1 result must correspond to a v1 sendTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3v1Sent Tff7b72> T79c0ff0Tb4b4b4, Ta5d6ff"Ta5d6ffPLI/GeoChat fixtures must still round-trip through the real v1 pipelineTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`v1 pass reports non-PLI non-chat drops as expected not as failures`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Te6edf3firmwareVersion Tff7b72= Tff7b72nullTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3runnerTb4b4b4.Te6edf3runAllTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3v1Results Tff7b72= Te6edf3hTb4b4b4.Te6edf3runnerTb4b4b4.Te6edf3resultsTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3protocol Tff7b72=Tff7b72= Te6edf3TakProtocolTb4b4b4.Te6edf3V1 Tb4b4b4}
T8b949e// marker_spot.xml is type "b-m-p-s-m" β not representable in the legacy v1 TAKPacket
T8b949e// schema (only PLI/"a-f-*" and chat/"b-t-f" are). This must show up as an *expected*
T8b949e// drop, not an unlabeled self-test failure, per the v1 schema's documented limitations.
Tff7b72val Te6edf3markerResult Tff7b72= Te6edf3v1ResultsTb4b4b4.Te6edf3single Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fixtureName Tff7b72=Tff7b72= Ta5d6ff"Ta5d6ffmarker_spot.xmlTa5d6ff" Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3markerResultTb4b4b4.Te6edf3expectedDropTb4b4b4, Ta5d6ff"Ta5d6ffv1 dropping an unsupported CoT type is expected, not a bugTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Tff7b72!Te6edf3markerResultTb4b4b4.Te6edf3passedTb4b4b4)
T8b949e// Every v1 drop must be accounted for: either the known schema-coverage limitation
T8b949e// (expectedDrop) or an oversize drop, which is a real MTU problem and correctly NOT
T8b949e// labeled expected even though it happened on v1 β see TakSendOutcome.Dropped.schemaLimited.
T8b949e// A drop that is neither would be a genuine, unaccounted-for failure.
Te6edf3assertTrueTb4b4b4(
Te6edf3v1ResultsTb4b4b4.Te6edf3filter Tb4b4b4{ Tff7b72!Tffa657itTb4b4b4.Te6edf3passed Tb4b4b4}Tb4b4b4.Te6edf3all Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3expectedDrop Tff7b72|Tff7b72| Tffa657itTb4b4b4.Te6edf3errorTff7b72?.Te6edf3startsWithTb4b4b4(Ta5d6ff"Ta5d6ffOversizedTa5d6ff"Tb4b4b4) Tff7b72=Tff7b72= Tff7b72true Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffevery v1 drop in a clean run should be the known schema limitation or an oversize drop, Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6ffnot an unaccounted-for failureTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`v2 pass has no expected drops - v2 must support every fixture type`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTestTb4b4b4(Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Te6edf3firmwareVersion Tff7b72= Tff7b72nullTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3runnerTb4b4b4.Te6edf3runAllTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3v2Results Tff7b72= Te6edf3hTb4b4b4.Te6edf3runnerTb4b4b4.Te6edf3resultsTb4b4b4.Te6edf3valueTb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3protocol Tff7b72=Tff7b72= Te6edf3TakProtocolTb4b4b4.Te6edf3V2 Tb4b4b4}
Te6edf3assertTrueTb4b4b4(
Te6edf3v2ResultsTb4b4b4.Te6edf3none Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3expectedDrop Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffv2 has no known schema gaps, so nothing should be labeled expectedTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.07s